/* Parser specification  */
/* Your name:     LUONG VINH THAO         */
/* Your id:           PUFMINF1209      */  

%{
open Ast;;

let rec mappingfunction idlist bktype = 
	match idlist with
  []->[]
  | a::t -> VarDecl(a,bktype) :: (mappingfunction t bktype)
%}

/* token declarations */
%token  BOOL BREAK CLASS CONTINUE DO DOWNTO ELSE EXTENDS FLOAT FOR IF INTEGER NEW REPEAT STRING THEN TO UNTIL WHILE
									RETURN VOID  NULL SELF 
									     
%token ADD SUB MUL FDIVI IDIVI MOD
%token EQUAL LESS GREAT LEQUAL GEQUAL NEQUAL AND NOT OR ASSIG ONEQUAL CONCAT
%token LSQUARE RSQUARE LPAREN RPAREN LBRACK RBRACK SEMICOLON COLON DCOLON DOT COMMA
%token<int> INT_LITERAL
%token<string> ID 
%token<string> STRING_LITERAL 
%token<float> FLOAT_LITERAL
%token<bool> BOOL_LITERAL

%token EOF

%nonassoc LESS GREAT LEQUAL GEQUAL  /* lowest precedence */
%nonassoc EQUAL NEQUAL

%left AND, OR
%left ADD,SUB
%left MUL, FDIVI, IDIVI,MOD 

%left CONCAT
%nonassoc NOT

%nonassoc UNARY
%nonassoc LSQUARE COLON RSQUARE
%left DOT
%nonassoc NEW /* highest precedence */
%nonassoc LOWER_ELSE
%nonassoc ELSE



/* Start symbol S's declaration (which symbol of the start symbol) */
%start program /*the entry point*/
%type <Ast.programAST> program 

%%

/* Rule definitions */

program:  
	declaration_list EOF { $1}
;
declaration_list: 
	| declaration declaration_list {$1 @ $2}
	|  declaration  {$1}
;
declaration: 
 	| attributes {$1}
	| classdecl {[$1]}
	| method_declaration {[$1]} 
;
attributes:
					|var SEMICOLON {$1}
					|consDeclare {[$1]}
 ;
var :idList COLON Type {mappingfunction $1 $3}
;

idList: ID COMMA idList{[$1] @ $3}
			|ID {[$1]}
;
consDeclare: ID ONEQUAL expr  SEMICOLON  {ConstDecl($1, $3)} 
; 

/*class declare*/
classdecl:
							|  CLASS ID EXTENDS ID LPAREN  list_member RPAREN {ClassDecl($2,$4,$6)}
							|  CLASS ID LPAREN  list_member RPAREN {ClassDecl($2,"",$4)}
;
list_member: 
						|attributes list_member {$1@ $2}
						| method_prototype list_member {[$1]@ $2}
						| {[]}
;
method_prototype:
						| bkType ID  LBRACK list_parameter  RBRACK SEMICOLON {MethodProDecl($1,$2,$4)}
						| ID  LBRACK list_parameter  RBRACK SEMICOLON {ConstructorDecl($1,$3)}
;		  
list_parameter:
						|var SEMICOLON list_parameter  {$1@ $3}
						|var {$1}
						| {[]}
;
/*method declare*/
method_declaration:
						| bkType ID DCOLON ID LBRACK list_parameter RBRACK LPAREN body RPAREN  {MethodDecl($1,$2,$4,$6,$9)}
						| ID DCOLON ID LBRACK list_parameter RBRACK LPAREN body RPAREN  {MethodDecl(ClassType($1),$1,$3,$5,$8)}
;
body:
		|list_attributes list_stmt {$1,$2}
;
list_attributes:
		| list_attributes attributes {$1 @ $2}
		| {[]}
list_stmt:
		| {[]}
		| stmt list_stmt {[$1] @ $2}
 ;
bkType:
		|Type {$1}
		| VOID {VoidType}
Type: 
			|elmentType {$1}
			|classType{$1}
			| arrayType {$1}

;
elmentType :
		| FLOAT {FloatType}
		| STRING {StringType}
		| BOOL {BoolType}
		| INTEGER {IntType}
		
;
classType:
	| ID{ClassType($1)}
 ;
arrayType:
		| elmentType LSQUARE INT_LITERAL RSQUARE {ArrayType($1,$3) } 
		| classType LSQUARE INT_LITERAL RSQUARE {ArrayType($1,$3)}
;
expr:  
			|ID {Lhs(Id($1))}
			|INT_LITERAL {Lit(IntVal($1))}
			| FLOAT_LITERAL {Lit(FloatVal($1))}
			| BOOL_LITERAL {Lit(BoolVal($1))}
			| STRING_LITERAL { Lit(StringVal($1))}
			| SELF		{Self}
			| NULL{ Lit (Null)}
			| expr ADD expr {Binary(Plus,$1,$3)} /* pri expr*/
			| expr SUB expr {Binary(Minus,$1,$3)}
			| expr MUL expr {Binary(Mul,$1,$3)}
			| expr FDIVI expr {Binary(Div,$1,$3)}
			| expr IDIVI expr {Binary(IDiv,$1,$3)}
			| expr MOD expr {Binary(Mod,$1,$3)}	 
			| expr AND expr {Binary(And,$1,$3)} /* Bool expr*/
			| expr OR expr {Binary(Or,$1,$3)}  	
			| expr  CONCAT  expr {Binary(Concat,$1,$3)} /* String expr*/
			| expr  EQUAL  expr {Binary(Eq,$1,$3)} /* relation expr*/
			| expr  NEQUAL  expr  {Binary(Neq,$1,$3)}   
			| expr GREAT expr {Binary(Gt,$1,$3)}
			| expr LESS expr {Binary(Lt,$1,$3)} 
			| expr GEQUAL expr {Binary(Ge,$1,$3)}
			| expr LEQUAL expr {Binary(Le,$1,$3)}
			| ADD expr %prec UNARY {Unary(UPlus,$2)}
			| SUB expr %prec UNARY {Unary(UMinus,$2)} 
			|  NOT expr {Unary(NotOp,$2)}  
			| LBRACK expr RBRACK{$2}
			| member_access{$1} 
			| index{$1}
			| object_create{$1}
;
member_access:
			| expr DOT ID {Lhs(Field($1,$3))} /*member access*/
			| expr DOT ID LBRACK list_argument RBRACK {CallExp($1,$3,$5)} 
;

index:
		 | expr LSQUARE expr RSQUARE {Lhs(Member($1,$3))}	/*index */
	;
object_create:
				| NEW ID LBRACK list_argument RBRACK {New($2,$4)}
;
list_argument:
						| expr COMMA  list_argument {[$1] @ $3}
						| expr {[$1]}
						| {[]}
;

stmt:
		|block_stmt{$1}
		| assignment_stmt {$1}
		| if_stmt{$1}
		| while_stmt{$1}
		| repeat_stmt{$1}
		| continue_stmt{$1}
		| for_stmt{$1}
		| break_stmt{$1}
		| return_stmt{$1}
		| method_invocate_stmt{$1}

	;
block_stmt:
	| LPAREN list_attributes list_stmt RPAREN{Block($2,$3)}
	;
/*assign*/
assignment_stmt :
		| lhs ASSIG expr SEMICOLON {Assign($1,$3)}
	;						
lhs:
		|ID  {Id($1)}
		| expr LSQUARE expr RSQUARE {Member($1,$3)}	
		| expr DOT ID {Field($1,$3)} /*member access*/
	;
	/*if statment*/
if_stmt:
		| IF expr THEN stmt {IfThen($2,$4)}  %prec LOWER_ELSE
		| IF expr THEN stmt ELSE stmt {IfThenElse($2,$4,$6)}
;
/*while statment conflit*/
while_stmt:
		| WHILE expr DO stmt {While($2,$4)}
	 ;
/*repeat statment*/
repeat_stmt:
		|REPEAT list_stmt UNTIL expr SEMICOLON {Repeat($2,$4)}
;
continue_stmt:
		|CONTINUE SEMICOLON{Continue}
;
for_stmt:
		| FOR ID ASSIG expr TO expr DO stmt  {For($2,$4,true,$6,$8)}
		|  FOR ID ASSIG expr DOWNTO expr DO  stmt  {For($2,$4,false,$6,$8)}
;
break_stmt:
		|BREAK SEMICOLON {Break}
 ;
return_stmt:
		| RETURN expr SEMICOLON {Return($2)}
	;
method_invocate_stmt:
		|expr DOT ID LBRACK list_argument RBRACK SEMICOLON {Call($1,$3,$5)}
	
;